chore: 工程门禁 — ESLint / Prettier / 覆盖率 / Dependabot / CI 矩阵 / e2e 冒烟 / 依赖升级 - #376
Open
oratis wants to merge 15 commits into
Open
chore: 工程门禁 — ESLint / Prettier / 覆盖率 / Dependabot / CI 矩阵 / e2e 冒烟 / 依赖升级#376oratis wants to merge 15 commits into
oratis wants to merge 15 commits into
Conversation
oratis
force-pushed
the
claude/opt-web-ui
branch
from
September 7, 2026 04:15
3eda740 to
3ffd015
Compare
oratis
force-pushed
the
claude/opt-tooling
branch
from
September 7, 2026 04:15
fb2f057 to
2b507b2
Compare
…baseline
T-4 (engineering gates): 60k lines of TypeScript had no linter. This adds
ESLint 10 + typescript-eslint 8 (recommended) plus the type-aware rules that
catch real bugs here — no-floating-promises, no-misused-promises,
await-thenable, no-unnecessary-type-assertion — and the house rules no-empty
(allowEmptyCatch: false), no-console (off for src/cli.ts, src/cli/**,
src/log.ts, scripts/**, tests), eqeqeq (null: ignore — `== null` is the
idiomatic nullish check throughout) and prefer-const. Scripts: `npm run lint`
(errors fail) and `npm run lint:fix`; CI runs lint after typecheck.
Type info comes from a dedicated tsconfig.eslint.json via parserOptions.project
instead of parserOptions.projectService: projectService only discovers files
that some tsconfig.json includes, and tsconfig.json (the build config, also run
verbatim by scripts/build-release.sh) excludes every *.test.ts. Its escape
hatch (allowDefaultProject) is capped at a handful of files and forbids `**`
globs, so it cannot carry 190+ test files. (The tests also do not typecheck
under noUncheckedIndexedAccess today, so folding them into tsconfig.json would
break `npm run typecheck`.)
Result today: `npm run lint` → 0 errors, 90 warnings. Warnings by rule:
43 no-console
16 @typescript-eslint/no-unnecessary-type-assertion
9 @typescript-eslint/no-explicit-any
6 no-empty
6 @typescript-eslint/no-misused-promises
6 no-useless-assignment
2 prefer-const
1 no-useless-escape
1 unused eslint-disable directive (src/soul/birth.ts)
Every warning is a pre-existing violation inside a file another work stream
is editing right now (server.ts, lisa-*.ts, birth.ts, cli*, billing/**, …)
or a console.* call whose migration to log.ts is a behaviour change. Those
are pinned to `warn` per file by the `baseline` table at the top of
eslint.config.js — shrink it, never grow it.
Mechanical fixes in files no other stream owns, verified by typecheck + the
full suite: 210 unnecessary `as`/`!` assertions removed (eslint --fix, type
program identical to tsc's), 9 empty catch blocks documented, 4 rethrows now
carry `{ cause }`, 13 unused imports/locals dropped, one `void`-wrapped
setInterval callback, one useless-escape and one regex-spaces fix, a
zero-width space in a doc comment replaced with `<project>`, and three stale
eslint-disable directives removed. No runtime behaviour changes.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit e9c4b388ac9223b86211d64d5610f880c3d0c0b4)
…gate T-4 (engineering gates): 60k lines of TypeScript had no formatter, so style was whatever the last editor's habits were and every PR carried unrelated whitespace noise. Config matches what the codebase already does, verified by sampling rather than assumed: 2-space indent (475 two-space vs 24 three-space leading runs across agent.ts / soul/store.ts / web/capabilities.ts / providers/anthropic.ts), double quotes (2,255 double-quoted vs 72 single-quoted literals), semicolons, trailing commas. printWidth 100: churn at 80/100/120 on five representative files is 176/200/287 changed lines — a wash between 80 and 100 — and 100 matches the observed p99 line length of 111 while keeping the existing long call signatures on one line. Deliberately NOT reformatting the repo. `prettier --check .` reports ~480 files today; rewriting them in one commit would explode every in-flight branch and destroy `git blame` on the whole tree. Instead `npm run format:check` (scripts/ format-check.mjs) diffs against the merge-base with the target branch — $FORMAT_BASE_REF, else origin/<GITHUB_BASE_REF or main>, else that branch — and only gates files this branch actually touched, plus the working tree and untracked files so `npm run format` fixes what you are about to commit. Files convert as they are edited and the repo converges without a big-bang commit. On a shallow clone with no base ref the gate skips rather than failing a build it cannot scope; CI checks out with fetch-depth: 0 so it never has to. That rule applies to this branch too, so the 87 files the ESLint commit (e9c4b38) already touched are formatted here. None of them are files another work stream owns — server.ts, lisa-client.ts, lisa-css.ts, birth.ts, cli*, billing/**, log.ts and the assets tree are all untouched. .prettierignore keeps out generated files, the vendored asset tree, website/ (own toolchain), research/, packaging/, deploy/, contracts/, all Markdown (hand-formatted tables and CJK spacing) and package*.json (npm owns those). .editorconfig mirrors the same settings for editors that read it before Prettier runs, with the two exceptions Prettier does not cover: Swift at 4 spaces and Makefile tabs. Verified: npm run typecheck, npm run lint (0 errors, 90 warnings — unchanged), npm test (1,645 tests, 1,644 pass / 1 skipped / 0 fail), npm run build, npm run format:check all green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> (cherry picked from commit 5746f5759fc6b54aba77a684dc5774a30d5f240b)
…modules T-4 (engineering gates): the repo had 1,645 tests and no way to see what they miss. `npm run test:coverage` now runs the suite under c8 and emits text-summary (console), lcov (tools) and json-summary (the gate below). Measured baseline, 2026-09-06, lines / branches / functions: repo total 73.63 / 79.74 / 80.49 src/billing/ (10 files) 84.89 / 80.49 / 90.91 src/web/accounts.ts 94.50 / 89.55 / 97.44 src/web/otp.ts 94.55 / 80.77 / 100.00 src/web/sessions-auth.ts 95.56 / 78.79 / 100.00 src/web/capabilities.ts 100.00 / 92.31 / 100.00 src/soul/store.ts 78.17 / 85.88 / 67.86 Floors are min(85, measured) rounded down per metric, so the gate is green today and can only be raised. c8's own --check-coverage could not express this: it is either one global number (the repo is at 74%, and lifting that to 85% is a long project, not a gate) or --per-file, which applies the same number to every one of 284 files. scripts/coverage-thresholds.mjs reads coverage-summary.json and checks the table instead — directory entries aggregate, file entries are exact, and a target that stops matching any file fails rather than passing silently after a rename. The list is deliberately short: these are the paths where a coverage regression means an untested branch in code that moves money, decides identity, or writes Soul state. Raise a floor when real coverage passes it; adding tests is the only correct way to make this gate pass. CI runs coverage as its own job (the numbers do not vary across the Node matrix, so running c8 three times would triple CI time for one report), uploads coverage/lcov.info as an artifact, and the script writes the floor table to $GITHUB_STEP_SUMMARY so the numbers are visible without downloading anything. c8 is a devDependency; no runtime dependency added. coverage/ is gitignored. Verified: npm run test:coverage green (1,645 tests, all floors met), npm run lint 0 errors, npm run format:check green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> (cherry picked from commit 4d28a39cbb10f3f448a2d0827c2f5fc4540aae88)
T-5 (v0.24.0 tech review): 24 releases with no update bot left @anthropic-ai/sdk 32 minors behind and three advisories open in transitive deps (fast-uri high, hono and qs moderate — all with fixes available). Dependabot covers five manifests: the root npm package weekly, website/ weekly (Astro deploys independently), packaging/gcp-relay monthly, github-actions monthly, and packaging/mac-client (SwiftPM) monthly. The iOS companion is XcodeGen with no SwiftPM dependencies, so there is nothing there to watch until it grows a Package.swift. Minor and patch updates are grouped into one PR per ecosystem. Ungrouped, a tree this size produces a dozen PRs a week and the bot gets muted; grouped, the weekly PR is one review and majors — which need real work in src/providers — still arrive individually. @types/node majors are excluded: those track `engines` and the CI Node matrix (20/22/24), and bumping them independently surfaces APIs the supported runtimes do not have. The CI audit job runs `npm audit --omit=dev --audit-level=high`. Production dependencies only — dev-tree findings are build tooling that never ships and never sees untrusted input, and gating on them makes the check noise. `high` is the bar for the same reason: moderate advisories in transitive deps can sit unfixable for weeks, and a permanently red gate teaches people to ignore it. This gate is red at this commit — fast-uri's high advisory is still present. The next commit in this series runs `npm audit fix`, which clears it; the gate is added first so the fix commit has something proving it worked. actionlint is unavailable in this environment (no Homebrew, and downloading a binary to run is not something I will do unprompted), so both YAML files were validated by parsing them with PyYAML and reviewed against the documented schemas instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> (cherry picked from commit 267cb054ce72f3aa8bd3dd70c98458bc2f8a92e2)
T-4 (v0.24.0 tech review, §6.16 from v0.21 before it): PR CI was one job on one Node version. website/, packaging/mac-client/ and packaging/ios-companion/ were only ever built by the release pipeline, so a PR could break any of them and nobody found out until a tag was cut. ci.yml now has: `changes` (computes which surfaces a PR touched), `core` (the Node matrix), `coverage`, `audit`, and three conditional jobs — `website` (Astro build + page assertions), `macos` (swift build -c debug) and `ios` (xcodegen + build.sh test on a simulator). Concurrency cancels superseded runs per ref, except on main, whose runs are what release tags are cut from. Path filtering is computed in a job rather than with `paths:`, which is workflow-wide and cannot gate individual jobs. It diffs against the PR base (or the push's `before`) and fails open — no usable base means run everything — because a filter that silently skips a native build is worse than a slow run. Editing ci.yml itself triggers all three, so a change to a job is proved by that job and not by a follow-up commit. The iOS job discovers the simulator instead of hardcoding one: build.sh defaults to "iPhone 17 Pro", and pinning a device name in CI breaks the day GitHub rolls the runner image. It asks the installed Xcode for its available iPhones and fails loudly, with the device list, if there are none. TWO FINDINGS, both fixed here: 1. `npm test` did not work on Node 20 at all. The script passed the glob "src/**/*.test.ts" to `node --test`, which only expands globs on Node 22+; Node 20 printed «Could not find 'src/**/*.test.ts'» and exited 1. scripts/ run-tests.mjs walks src/ and passes explicit paths, so one command works across the matrix and does not depend on sh vs cmd globbing. Extra args still forward (`npm test -- --test-name-pattern=soul`). 2. With that fixed, Node 20 fails 26 tests: undici 8.9.0 — a *production* dependency — declares `engines: node >=22.19.0`, and its webidl layer calls worker_threads' markAsUncloneable, absent before Node 22.10. Verified on 20.20.2. So `engines: >=20.0.0` was already a false promise: installing on Node 20 succeeds and then dies at runtime inside undici. `engines` is now >=22.19.0, matching undici, the CI matrix (22/24) and @types/node ^22. This is user-visible — `npm i -g @oratis/lisa` on Node 20 now fails at install with a clear message instead of at runtime with a confusing one — and it is the honest version of what the package already required. src/cli/doctor.ts:48 still prints "need ≥ 20"; that file belongs to another work stream right now and is left for them. actionlint could not be installed here (no Homebrew in this environment), so the workflow was validated by parsing it with PyYAML, and the two non-trivial shell snippets — the path filter and the simulator picker — were executed locally against synthetic inputs covering src-only, native-only, ci.yml and empty diffs. The job formerly called `check` is now `core`; branch protection's required checks need updating to match. Verified: npm run typecheck, npm run lint (0 errors, 90 warnings), npm run format:check, npm test (1,645 tests, 1,644 pass / 1 skip / 0 fail), npm run build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> (cherry picked from commit 4a2a06bd7c3448afc80ed3ebf6092ceb56225694)
T-4 / UX-2 (v0.24.0 reviews): RELEASE_v0.23 described a "real-browser E2E" pass
that never landed in the repo — `playwright|puppeteer` had zero hits. The first
run, the birth ritual and the mobile breakpoints were all verified by hand, once,
by a human reading a browser.
23 specs, ~19s, zero model calls, zero network:
key-gate an unconfigured home shows the key gate and NOT the birth ritual
main-shell identity card / 3x3 nav / session tree / composer; view switching;
theme toggle persisting lisa-theme across a reload; +New adding a
tree leaf
layout 375x812, 768x1024, 1440x900 x rail collapsed/expanded — no
horizontal page scroll, all shell regions have real width
birth 401 → the ritual surfaces a failure and leaves isBorn() false;
success → every step streams through to "done", ENTER lands in the
chat view, and the dreamed soul is on disk
Determinism comes from three pieces:
- helpers/stub-anthropic.ts stands in for api.anthropic.com. The SDK honours
ANTHROPIC_BASE_URL (registry.ts passes it through), so pointing that at a
local stub is the whole trick. It emits the exact event sequence
MessageStream needs — message_start, content_block_start, two text_deltas
(a split payload, so the accumulator is actually exercised),
content_block_stop, message_delta, message_stop — or a 401
authentication_error in the shape the real API returns.
- helpers/make-soul.ts fabricates a born soul through src/soul/store.ts, in
birth.ts's exact write order: everything else first, seed.json last (it is
the isBorn() flip), then the lock. It runs as its own tsx process because
soul paths resolve LISA_HOME at call time.
- helpers/lisa-server.ts gives each spec file a throwaway LISA_HOME *and* HOME
under .tmp/e2e, a free port, and `node dist/cli.js serve --web --no-idle
--no-reflect --no-mcp --no-plugins`. A separate HOME matters: it is what
keeps the claude-code watcher off the operator's real ~/.claude. GIT_AUTHOR_*
and GIT_COMMITTER_* are set because the soul store commits on every write and
a temp HOME has no ~/.gitconfig.
The global setup only builds — dist/, not tsx, because a smoke test that passes
against the dev loader and not the shipped artefact is worth nothing. Servers
are per spec file: the four scenarios need four different homes (no soul + no
key, fabricated soul, empty home + failing stub, empty home + working stub), so
one shared instance cannot serve them.
FOUR ASSERTIONS ARE test.fixme, all describing behaviour another stream is
fixing right now, all verified to fail against this worktree today:
UX-2 · .main is 75px at 375px wide with the rail collapsed (expected 375) —
exactly the number the UX review measured — and with the rail open the
send button ends at x=665, off a 375px screen.
UX-1 · #birthError renders `401 {"type":"error","error":{"type":
"authentication_error",…}}` verbatim, and there is no Change key
button anywhere in the DOM.
Flip those four from test.fixme to test after the UX fixes integrate; they are
the acceptance criteria for UX-1 and UX-2 written down.
Chromium only (this is a localhost app, not a public website — a three-browser
matrix triples the slowest CI job for little), retries 1 on CI, trace on first
retry. CI installs the browser with --with-deps, typechecks the specs first
(tests/e2e/tsconfig.json exists because these are the only files in the repo
that are both Node and DOM), and uploads the HTML report on failure.
Verified: full suite 23 passed / 4 skipped in 19s; npm run typecheck,
npm run typecheck:e2e, npm run lint (0 errors, 90 warnings),
npm run format:check, npm test (1,645) and npm run build all green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 671040fa5e6ec0fae7d8bcd9830ac001217fccb5)
… 6.49
T-5 wave 1, step 1 of 3. `npm audit fix` clears all three advisories the review
found — fast-uri (high, SSRF-adjacent), hono and qs (moderate), all transitive
through @modelcontextprotocol/sdk. `npm audit --omit=dev` now reports 0
vulnerabilities, so the CI audit gate added two commits ago goes green.
Minor/patch updates:
@google/genai 2.13.0 → 2.21.0
@modelcontextprotocol/sdk 1.29.0 → 1.30.0
imapflow 1.4.2 → 1.7.8
music-metadata 11.14.0 → 11.15.0
openai 6.35.0 → 6.49.0
sharp 0.35.3 → 0.35.4
tsx 4.23.1 → 4.23.13
undici 8.9.0 → 8.10.2
@types/node 22.19.17 → 22.20.1 (stays on 22.x: it tracks the
CI matrix floor, not the newest
Node)
openai 6.49 forced one source change. src/voice/transcribe.ts passed
`fs.createReadStream(audioPath)` to audio.transcriptions.create; the new SDK
does not consume the stream before the request settles, so the ReadStream's
async open landed after the test had deleted its temp file — «generated
asynchronous activity after the test ended … ENOENT». The fix is not a test
workaround: a ReadStream that nothing consumes leaks its descriptor, so any
failed transcription request leaked an fd. Reading the clip and handing the SDK
a File via toFile() removes both the race and the leak, and clips are already
length-capped by maxTranscriptionSeconds() so buffering one is bounded.
Also de-flaked src/mood-bus.test.ts: the mirror-file poll was 50 iterations of
10ms, which loses the race under `npm run test:coverage` where c8's
instrumentation slows every write. It now polls to a 10s deadline. Caught by
running the coverage job, not the plain suite — which is the point of having it.
Verified on BOTH runtimes in the CI matrix: npm test 1,645 tests, 1,644 pass /
1 skip / 0 fail on Node 24.12 and on Node 22 (`npx -y -p node@22 npm test`).
Plus npm run typecheck, npm run lint (0 errors, 90 warnings),
npm run format:check, npm run build, npm run test:coverage (all floors met) and
the Playwright suite (23 passed / 4 fixme).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit d9fc6d0b99a3052a11e7de33f91e5d0680ccd9c7)
… forced
T-5 wave 1, step 2 of 3. The SDK was 32 minors behind — new model and tool
capabilities were reachable only by casting around types that predated them.
The upgrade is source-compatible: typecheck, the 54 provider tests and the full
1,645-test suite pass unchanged. What changed is that three workarounds are no
longer needed, and lint's no-unnecessary-type-assertion count drops from 90 to
88 warnings on its own.
Removed:
- `output_config.effort` was written through a double cast
(`(params as { output_config?: { effort?: string } })`) because 0.92 had no
such field. It is now `OutputConfig` on MessageCreateParams with `effort`
typed as 'low'|'medium'|'high'|'xhigh'|'max' — the same union
ProviderRunOpts already declares — so it is a plain assignment. The Haiku
gate (modelSupportsEffort) stays: that is an API behaviour, not a type gap.
- The compaction extras were `{ betas?: string[]; context_management?: object }`
— a hand-written shape standing in for types that did not exist. They are now
`Pick<Anthropic.Beta.Messages.MessageCreateParamsStreaming, "betas" |
"context_management">`, so a wrong beta name or a malformed edit is a compile
error instead of a 400 at runtime.
- StreamLike.finalMessage() returned `Promise<unknown>`; it now returns
`Message | BetaMessage`, which is what the two endpoints actually return.
Kept, with the reason written down: the `as Anthropic.Message` at the end of
runTurn. BetaMessage's content is a superset of Message's, and ProviderResult
declares Anthropic.ContentBlock[] — that narrowing is inherent to supporting
both the beta (compaction) and stable endpoints from one code path, not a
leftover. Streaming, tool use, thinking, effort, the compaction beta and abort
all keep working; the existing tests cover each.
Also de-flaked src/mood-bus.test.ts properly. Raising its poll budget (previous
commit) was the wrong diagnosis: it waited the full 10s and still saw nothing,
because persist() is best-effort by design and swallows every error — under a
loaded full-suite run its single fire-and-forget write can be dropped outright,
and no amount of waiting conjures the file. The test now re-issues the same set
on each pass, which is exactly what the production path does (memory is the
source of truth, the next set re-persists), so it asserts the mirroring
behaviour instead of one syscall's luck. Three consecutive full-suite runs
green.
Verified: npm test 1,645 / 1,644 pass / 1 skip / 0 fail on Node 24 AND Node 22;
npm run typecheck, lint (0 errors, 88 warnings — down from 90), format:check,
build, test:coverage (all floors met) and Playwright (23 passed / 4 fixme).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 88bc9a60b7a77dbbf6436fbbe2b963eea1010d5a)
T-5 wave 1, step 3 of 3 (the first of the two stretch upgrades). Kept because every gate is green with no source change at all: typecheck, npm run lint (0 errors, 88 warnings — unchanged), npm test 1,645 / 1,644 pass / 1 skip / 0 fail on BOTH Node 24 and Node 22, npm run build, the Playwright suite (23 passed / 4 fixme) and npm audit --omit=dev (0 vulnerabilities). src/providers/openai.ts and src/voice/transcribe.ts are the only consumers and neither needed touching — the 6.49 work in the first commit of this series (toFile instead of a ReadStream) had already moved transcribe off the API that was going to be the sharp edge. openai 7 declares `engines: node >=22.0.0`, which is consistent with this package's own floor of >=22.19.0 (set by undici). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> (cherry picked from commit 1ae2ef1de60bec0755c602d459a3a07f4a2710c5)
T-5 wave 1, the second stretch upgrade: attempted, measured, and rejected. The
finding is recorded where it is actionable — Dependabot will not open a
TypeScript major PR that cannot merge.
TypeScript 7.0.2 is ready on the language side. Measured on this tree:
npm run typecheck green
npm run build green
dist/ equivalence 284 .js and 284 .d.ts files, byte-identical to the 5.9.3
output. The only 161 differing files are *.js.map (108)
and *.d.ts.map (53) — source-map mappings, expected from a
different compiler emitting the same semantics.
It is blocked on tooling, not on this codebase: typescript-eslint 8 refuses to
load at all against TS 7 —
Error: typescript-eslint does not support TS 7.0.
at node_modules/typescript-eslint/dist/index.js:52
— so `npm run lint` dies before linting a single file. npm also has to override
a peer dependency to install the pair. Trading the linter this branch just
introduced for a compiler that emits identical output is a bad deal, so
TypeScript stays on ^5.7.0 (5.9.3 resolved).
Revisit when typescript-eslint ships TS >=7 support
(typescript-eslint/typescript-eslint#10940); the alternative, running
typescript-eslint against a side-by-side TS 6 install, is more moving parts than
this buys today.
Verified after reverting: npm run typecheck, npm run lint (0 errors, 88
warnings), npm run format:check, npm test (1,645 / 1,644 pass / 1 skip / 0 fail),
npm run build, npm audit --omit=dev (0 vulnerabilities) — all green, and
package-lock.json is back to exactly what `npm ci` installs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 2a40d021b666c7f6df9cb320a5fb89807eb0f98d)
The comment justifying the absence of Node 20 named undici 8.9; the audit-fix commit moved it to 8.10.2. The constraint is unchanged — every 8.x declares engines >=22.19.0 — but a comment that cites a version should cite the one in the lockfile. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> (cherry picked from commit a0e70629909847bf31b9d6c219a37d9a5fe5b0ee)
Each stream was green on its own branch, but the ESLint baseline was computed against a tree that did not yet contain the other seven streams' new files. Integrated, 37 errors appeared in code none of the streams could have linted. Twelve were mechanical (`--fix`); the rest are here, by kind: **`no-console` in src/billing/reconcile.ts (11).** `cmdBillingReconcile` moved to src/cli/billing-reconcile.ts. Everything it does beyond calling reconcileOnce() is printing, and a reconciler that writes to stdout from inside library code cannot be called from a request handler or a timer without polluting the log — which is precisely why src/billing keeps `no-console` at error. The library half keeps its structured logInfo/logError calls. **Empty catches (6).** Each now states the condition it is swallowing: a double close during log rotation, a missing rotation generation, a log file moved out from under us, a directory without .git while walking up, an unresolvable argv[0]. Matching the house style of explaining why, not what. **ANSI escapes in regexes (4).** src/cli/render.test.ts asserts on real `\x1b[…m` sequences because emitting or suppressing them is the thing under test. Rule disabled for that file with the reason. **`any` in types/web-client.d.ts (4).** The ambient file exists so `tsc --checkJs` can run over the extracted client bundle; its loose index signatures are the mechanism, not an oversight. Rule disabled with the reason, and the file joined the lint program (tsconfig.eslint.json) so everything else in it is still checked. **Dead references (3).** `capabilityProfileForEdition` (superseded by the per-surface profiles), `lastPromptFingerprintIn` (orphaned by the streamed readMessagePage rewrite), and an unused test parameter. Also merged three overlapping edits the streams made to the same lines: package.json keeps all four new scripts; src/tools/registry.ts keeps the cast-free list plus the archived doc path; src/channels/router.ts keeps `sandboxModeForProfile` over the reformatted older call; src/sessions/store.ts keeps the streamed bounded-ring page reader over the reformatted readFile one; and the extracted client bundle carries the docs/archive/plans/ paths the docs stream fixed in the template literals it replaced. Verified on the integrated tree: typecheck, typecheck:client, lint (0 errors, 69 warnings — all pre-existing baseline entries), check:api-contract, build with no test files in dist, and 1,951 tests passing (0 failures, 1 PTY skip). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
oratis
force-pushed
the
claude/opt-web-ui
branch
from
September 7, 2026 04:24
3ffd015 to
7f7dbde
Compare
oratis
force-pushed
the
claude/opt-tooling
branch
from
September 7, 2026 04:24
2b507b2 to
8a0a300
Compare
The knowledge base should carry the outcome, not just the diagnosis: which PR holds which theme, the verification numbers on the integrated tree, and — the part worth remembering — the three defects that only appeared once the eight streams were in one tree. The birth-timer one is the instructive case. Every stream was green on its own branch; the bug needed the Node 20/22/24 matrix, which arrived in a different stream, to become visible at all. That is the argument for the matrix, and the reason this file now says so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The format gate compares against origin/main and requires every changed file to be Prettier-clean. Eight streams wrote these 62 files in parallel worktrees, all of them before the Prettier config existed on any branch, so the gate failed the moment it first ran on the integrated tree — which is the gate working. It is one commit at the end of the chain rather than one per stream on purpose. Formatting a file on an early branch that a later branch then rewrites (server.ts and the client are edited by three streams between them) buys nothing but rebase conflicts, and the whole chain lands together anyway. src/web/assets/ is in .prettierignore, so the extracted client bundle is untouched and the byte-level HTML composition tests still hold. Formatting only: no behaviour changed. typecheck, lint, check:api-contract, build and 1,951 tests are green after it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…th about Node Three things the integrated tree exposed that no single branch could. **The key-gate specs tested markup that no longer exists.** They were written against `#cfgAnthropic`, the Anthropic-only field; the web-UI branch replaced the gate with a provider picker (`#cfgProvider` + `#cfgKey`) in the same chain. Retargeted, and while there, the spec now also asserts the picker is actually populated — an empty <select> would leave a first-time user with a key field labelled for a provider they may not have an account with, which is the dead end UX-6 set out to remove. **Four test.fixme assertions are now live.** They were parked because the behaviour they describe lived on a branch the tooling stream could not see: the two UX-2 mobile assertions (.main fills the viewport, SEND is on screen at 375px) and the two UX-1 birth assertions (a human error, a Change key button back to the gate). All four fixes landed earlier in this chain, so the tests are enabled — and they are what stops those two P0s from coming back. 27/27 green locally. **`engines` moved to >=22.19.0 but three places still promised Node 20.** The floor moved for a real reason (undici calls worker_threads APIs added in 22.10, so `>=20.0.0` was a false promise that failed at runtime instead of at install). Left unaligned, a Node 20 user read "Node ≥ 20" in the README or on the install page, ran npm i, and got refused. `lisa doctor` was worse: its check *passed* on Node 20, so the tool told them their environment was fine minutes before it broke. The check now tests major.minor, and the README, the Chinese README and both install pages say 22.19. Verified: typecheck, lint, format:check, 1,951 unit tests, 27 e2e, website. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
第 8 / 8 条,实现 T-4 与 T-5。基于 #375。
门禁(T-4)
6 万行 TypeScript 此前没有 lint、没有 formatter、没有覆盖率、没有依赖机器人,PR CI 只跑 Node 一个表面。
no-floating-promises、no-misused-promises等),配合no-empty、no-console、eqeqeq、prefer-const。现存违规进 warn 基线(69 条),新代码必须在 error 级通过。min(85, 实测)),CI 上传 lcov 并写入 job summary。npm audit --omit=dev --audit-level=high门禁。website/**触发 Astro 构建;packaging/mac-client/**触发 macOS runner 上的 swift build;packaging/ios-companion/**触发模拟器测试。此前后三者只在发布流水线里验证。ANTHROPIC_BASE_URL)分别返回 401 或预设的流式响应,覆盖密钥面板、主外壳、主题持久化、新建会话、三个断点下的布局、出生失败与成功两条路径。依赖(T-5)
npm audit fix+ 全部 minor/patch 更新@anthropic-ai/sdk0.92 → 0.124(落后 32 个 minor),并删掉当初为旧类型添加的断言openai6 → 7集成修复
最后一个提交处理八条流合并后才出现的 37 个 lint 错误——每条流在自己分支上都是绿的,但基线是在看不到其他七条流新文件的树上算出来的。其中把
cmdBillingReconcile从src/billing/移到src/cli/:它除了调用reconcileOnce()之外全部工作都是打印,而会打印的库代码无法被请求处理器或定时器调用——这正是src/billing保持no-console: error的理由。集成后的完整验证
npm test🤖 Generated with Claude Code